from core.spider.pure_api_zero import ApiZeroSeven
import threading
import time
import logging
from datetime import datetime, timedelta
from config_loader import ConfigLoader, get_full_config_path, get_global_config
from notify.audio_sender import AudioSender
from notify.email_sender import EmailSender
import os
from db.entity2 import get_base_dir

# Configure logger
logger = logging.getLogger(__name__)

class OddsMonitor:
    """盘口差异监控系统"""

    def __init__(self, match_viewer):
        self.match_viewer = match_viewer
        self.monitored_matches = set()  # 监控的比赛ID集合
        self.monitoring_companies = set()  # 监控的公司集合
        self.odds_threshold = 0.25  # 默认盘口差异阈值
        self.is_monitoring = False
        self.monitor_thread = None
        self.api = ApiZeroSeven()

        # 公司名称映射 (Name -> ID)
        self.company_map = {
            '澳门': '1',
            '皇冠': '3',
            'Bet365': '8',
            # '易胜博': '12',
            # '韦德': '14',
            '立博': '4',
            '威廉希尔': '9',
            'Pinnacle': '177',
            # '利记': '31',
            # '盈禾': '35',
            # '18Bet': '42'
        }

        # 初始化公司列表（防止AttributeError）
        self.companies = list(self.company_map.keys())




        # 从配置文件加载所有监控配置
        self.load_config()

        # 缓存最新的盘口数据 {match_id: {company: handicap}}
        self.odds_data = {}
        
        # 记录最后通知时间 {match_id: datetime}
        self.last_notification_time = {}

        # 走地异常监控配置
        self.enable_anomaly_monitor = False
        self.anomaly_min_initial = 5.0  # 初盘大球最小值
        self.anomaly_max_current = 2.0  # 即时大球最大值 (中场)
        self.anomaly_time_start = 40    # 监控开始时间(分钟)
        self.anomaly_time_end = 50      # 监控结束时间(分钟)

        # 新增监控开关
        self.monitor_handicap_diff = True     # 监控亚让差异
        self.monitor_total_goals_diff = False # 监控总进球差异
        self.auto_add_live = True             # 自动添加走地比赛

    def load_config(self):
        """从配置文件加载所有监控配置"""
        config_path = get_full_config_path()
        try:
            config = ConfigLoader.load_config(config_path)
            
            # 1. 加载公司配置
            default_companies = config.get("monitor.companies", [])
            if not default_companies:
                default_companies = []
            self.set_monitoring_companies(default_companies)
            
            # 2. 加载监控通用配置
            self.odds_threshold = config.get("monitor.threshold", 0.25)
            self.monitor_handicap_diff = config.get("monitor.handicap_diff", True)
            self.monitor_total_goals_diff = config.get("monitor.total_goals_diff", False)
            self.auto_add_live = config.get("monitor.auto_live", True)
            

            
            logger.info("成功加载监控配置")
        except Exception as e:
            logger.error(f"加载配置失败: {e}")

    def save_config(self):
        """保存所有配置到配置文件"""
        config_path = get_full_config_path()
        try:
            config = get_global_config()
            
            # 1. 保存公司
            config.set("monitor.companies", list(self.monitoring_companies))
            
            # 2. 保存监控通用配置
            config.set("monitor.threshold", self.odds_threshold)
            config.set("monitor.handicap_diff", self.monitor_handicap_diff)
            config.set("monitor.total_goals_diff", self.monitor_total_goals_diff)
            config.set("monitor.auto_live", self.auto_add_live)
            
            # 3. 保存异常监控配置
            config.set("anomaly.enabled", self.enable_anomaly_monitor)
            config.set("anomaly.min_initial", self.anomaly_min_initial)
            config.set("anomaly.max_current", self.anomaly_max_current)
            config.set("anomaly.time_start", self.anomaly_time_start)
            config.set("anomaly.time_end", self.anomaly_time_end)
            


            config.save(config_path)
            self._log_to_ui("配置已保存到文件", "INFO")
        except Exception as e:
            self._log_to_ui(f"保存配置失败: {e}", "ERROR")


    def add_monitored_match(self, match_id):
        """添加监控的比赛"""
        if match_id in self.monitored_matches:
            return False

        self.monitored_matches.add(match_id)
        self._log_to_ui(f"开始监控比赛 {match_id}", "INFO")
        return True

    def add_all_matches(self):
        """将所有比赛添加到监控列表"""
        if not hasattr(self.match_viewer, 'match_data'):
            return

        added_count = 0
        for match in self.match_viewer.match_data:
            # 根据match对象类型获取比赛ID
            if hasattr(match, '比赛ID'):
                match_id = match.比赛ID
            elif isinstance(match, list) and len(match) > 1:
                match_id = match[1]
            else:
                continue

            if match_id and self.add_monitored_match(match_id):
                added_count += 1

        self._log_to_ui(f"已将 {added_count} 场比赛添加到监控列表", "INFO")

    def remove_monitored_match(self, match_id):
        """移除监控的比赛"""
        if match_id in self.monitored_matches:
            self.monitored_matches.remove(match_id)
            self._log_to_ui(f"停止监控比赛 {match_id}", "INFO")
            return True
        return False

    def set_monitoring_companies(self, companies):
        """设置监控的公司列表"""
        self.monitoring_companies = set(companies)
        company_list = ", ".join(companies)
        self._log_to_ui(f"监控公司设置为: {company_list}", "INFO")

    def set_odds_threshold(self, threshold):
        """设置盘口差异阈值"""
        self.odds_threshold = threshold
        self._log_to_ui(f"盘口差异阈值设置为: {threshold}", "INFO")

    def set_anomaly_config(self, enabled, min_init, max_curr, t_start, t_end):
        """设置异常监控参数"""
        self.enable_anomaly_monitor = enabled
        self.anomaly_min_initial = float(min_init)
        self.anomaly_max_current = float(max_curr)
        self.anomaly_time_start = int(t_start)
        self.anomaly_time_end = int(t_end)
        
        if enabled:
            self._log_to_ui(f"异常监控已开启: 初盘>={min_init}, 即时<={max_curr}, 时间{t_start}-{t_end}分", "INFO")
        else:
            self._log_to_ui(f"异常监控已关闭", "INFO")
    def calculate_odds_range(self, match_id):
        """计算指定比赛的盘口差异范围 (用于状态显示)"""
        if match_id not in self.odds_data:
            return 0, 0, 0  # min, max, range

        match_odds = self.odds_data[match_id]
        if not match_odds:
             return 0, 0, 0

        # 这里的 match_odds 是 {Company: Value}
        vals = list(match_odds.values())
        if not vals:
            return 0, 0, 0
            
        min_odds = min(vals)
        max_odds = max(vals)
        return min_odds, max_odds, max_odds - min_odds

    def update_odds_randomly(self):
        """随机更新盘口数据（模拟实时数据变化） - 已废弃"""
        pass

    def check_odds_alerts(self):
        """检查盘口差异告警 (手动检查用)"""
        if not self.monitored_matches:
             return []
             
        # 手动触发一次所有监控比赛的检查
        for mid in list(self.monitored_matches):
             self.check_match_odds(mid)
        return []

    def get_match_info(self, match_id):
        """根据比赛ID获取比赛信息"""
        # 从 match_viewer.match_data 中查找
        # match_data 是 MatchDataV2 对象列表 或 格式化后的列表
        for match in self.match_viewer.match_data:
            # 检查是否是对象
            if hasattr(match, '比赛ID') and str(match.比赛ID) == str(match_id):
                 return f"{match.主队名_简体} vs {match.客队名_简体} | ID:{match_id}"
            # 检查是否是列表 [Time, ID, Status, Match, Score, Half, Handicap, League, Note, Monitor]
            elif isinstance(match, list) and len(match) > 3 and str(match[1]) == str(match_id):
                 # match[3] 是 "主队 vs 客队" 字符串
                 return match[3]
        return f"比赛 {match_id}"

    def start_monitoring(self):
        """开始监控"""
        if self.is_monitoring:
            self._log_to_ui("监控已在运行中", "WARN")
            return

        self.is_monitoring = True
        self.monitor_thread = threading.Thread(target=self._monitoring_loop, daemon=True)
        self.monitor_thread.start()
        self._log_to_ui("盘口差异监控已启动", "INFO")

    def stop_monitoring(self):
        """停止监控"""
        self.is_monitoring = False
        if self.monitor_thread:
            self.monitor_thread.join(timeout=1)
        self._log_to_ui("盘口差异监控已停止", "INFO")

    def check_match_odds(self, match_id):
        """检查单场比赛的盘口"""
        try:
            # self._log_to_ui(f"正在获取比赛 {match_id} 亚让盘口...", "DEBUG")
            df = self.api.get_match_yarang(match_id)
            
            if df is None or df.empty:
                 return

            # 过滤监控的公司
            # monitoring_companies 存储的是公司名称
            target_ids = [str(self.company_map[c]) for c in self.monitoring_companies if c in self.company_map]
            
            if not target_ids:
                return
                
            # df['公司'] 是 ID, 确保是字符串比较
            df['公司'] = df['公司'].astype(str)
            relevant_odds = df[df['公司'].isin(target_ids)]
            
            if relevant_odds.empty:
                return

            # 提取盘口数据 ("即时进球数" 列对应亚盘盘口)
            current_handicaps = {}
            
            for _, row in relevant_odds.iterrows():
                try:
                    val_str = str(row['即时进球数'])
                    # 如果是空字符串或无效值，跳过
                    if not val_str or val_str.strip() == '':
                        continue
                        
                    # 处理分数盘口，虽然util里可能已经处理了，这里再保险一下
                    if '/' in val_str:
                         parts = val_str.split('/')
                         val = (float(parts[0]) + float(parts[1])) / 2
                    else:
                         val = float(val_str)
                    
                    cid = str(row['公司'])
                    # 找回公司名称
                    cname = next((k for k, v in self.company_map.items() if str(v) == cid), cid)
                    current_handicaps[cname] = val
                except (ValueError, TypeError):
                    continue
            
            if not current_handicaps:
                return

            # 更新内存中的数据供状态显示
            self.odds_data[match_id] = current_handicaps

            vals = list(current_handicaps.values())
            if not vals:
                return
                
            min_h = min(vals)
            max_h = max(vals)
            spread = max_h - min_h
            
            # 只有当差异超过阈值时才报警
            if spread >= self.odds_threshold:
                 match_info = self.get_match_info(match_id)
                 msg = f"盘口差异告警: {match_info}\n差异: {spread} (阈值: {self.odds_threshold})\n详情: {current_handicaps}"
                 self._show_alert(msg, match_id)
            
            # 记录一次成功的检查（可选，避免日志太多）
            # self._log_to_ui(f"比赛 {match_id} 盘口检查完成，差异: {spread}", "DEBUG")

        except Exception as e:
            err_msg = f"轮询比赛 {match_id} 失败: {e}"
            # print(err_msg) # Print to console
            self._log_to_ui(err_msg, "ERROR")

    def set_monitor_config(self, handicap_diff, total_goals_diff, auto_live):
        """设置监控配置"""
        self.monitor_handicap_diff = handicap_diff
        self.monitor_total_goals_diff = total_goals_diff
        self.auto_add_live = auto_live
        
        log_msg = f"监控配置更新: 亚让差异[{'开' if handicap_diff else '关'}], 总进球差异[{'开' if total_goals_diff else '关'}], 自动加走地[{'开' if auto_live else '关'}]"
        self._log_to_ui(log_msg, "INFO")

    def scan_for_live_matches(self):
        """扫描并自动添加走地比赛"""
        if not self.auto_add_live or not hasattr(self.match_viewer, 'match_data'):
            return

        try:
            count = 0
            for match in self.match_viewer.match_data:
                match_id = None
                is_live = False
                home_team = ""
                away_team = ""

                # 识别 MatchDataV2 对象
                if hasattr(match, '比赛ID'):
                    state = match.比赛状态
                    # 状态 1:上半场, 2:中场, 3:下半场
                    if state in [1, 2, 3]:
                        is_live = True
                        match_id = match.比赛ID
                        home_team = match.主队名_简体
                        away_team = match.客队名_简体
                # 识别列表格式 (如果存在)
                elif isinstance(match, list) and len(match) > 4:
                     # 假设 match[4] 是状态文本 or match[2] 是状态ID? 
                     # 这里依赖具体实现，先主要支持 MatchDataV2
                     pass

                if is_live and match_id and match_id not in self.monitored_matches:
                    self.add_monitored_match(match_id)
                    self._log_to_ui(f"自动添加走地比赛: {home_team} vs {away_team} (ID: {match_id})", "INFO")
                    # 更新列表中的热门/监控状态显示
                    if hasattr(match, '是否热门'):
                         match.是否热门 = True
                    count += 1
            
            if count > 0:
                 # 通知UI更新（如果需要刷新列表图标）
                 # self.match_viewer.populate_data() # 这可能会导致重绘，视情况而定
                 pass
                 
        except Exception as e:
            logger.error(f"自动扫描走地比赛失败: {e}")

    def check_match_overdown_odds(self, match_id):
        """检查单场比赛的总进球盘口 (大小球)"""
        try:
            df = self.api.get_match_overdown(match_id)
            
            if df is None or df.empty:
                 return

            target_ids = [str(self.company_map[c]) for c in self.monitoring_companies if c in self.company_map]
            if not target_ids:
                return
                
            df['公司'] = df['公司'].astype(str)
            relevant_odds = df[df['公司'].isin(target_ids)]
            
            if relevant_odds.empty:
                return

            current_handicaps = {}
            
            for _, row in relevant_odds.iterrows():
                try:
                    val_str = str(row['即时进球数'])
                    if not val_str or val_str.strip() == '':
                        continue
                        
                    if '/' in val_str:
                         parts = val_str.split('/')
                         val = (float(parts[0]) + float(parts[1])) / 2
                    else:
                         val = float(val_str)
                    
                    cid = str(row['公司'])
                    cname = next((k for k, v in self.company_map.items() if str(v) == cid), cid)
                    current_handicaps[cname] = val
                except (ValueError, TypeError):
                    continue
            
            if not current_handicaps:
                return

            vals = list(current_handicaps.values())
            if not vals:
                return
                
            min_h = min(vals)
            max_h = max(vals)
            spread = max_h - min_h
            
            if spread >= self.odds_threshold:
                 match_info = self.get_match_info(match_id)
                 msg = f"总进球差异告警: {match_info}\n差异: {spread} (阈值: {self.odds_threshold})\n详情: {current_handicaps}"
                 self._show_alert(msg, match_id)

        except Exception as e:
            self._log_to_ui(f"轮询总进球 {match_id} 失败: {e}", "ERROR")

    def check_match_anomaly(self, match_id):
        """检查单场比赛的走地异常 (大球初盘vs即时)"""
        try:
            # 1. 检查时间是否在监控范围内
            # 我们需要获取比赛的当前进行时间。
            # 由于 match_viewer.match_data 可能不是实时更新的，这里可能需要依赖 get_match_info 或其他方式
            # 暂时假设从 self.match_viewer.match_data 获取，虽然可能有一点延迟
            # 或者我们直接从 API 获取 OverDown 数据，虽然那里没有时间
            
            # 为了简单起见，我们先获取 Over/Under 数据，如果存在数据再进一步判断
            # 注意：准确的比赛进行时间最好是在 get_today_match_bf_data 里获取的，但那个是大列表
            
            df = self.api.get_match_overdown(match_id)
            if df is None or df.empty:
                return

            # 默认取皇冠(3)或Bet365(8)或第一家
            target_company_ids = ['3', '8', '1'] 
            
            # 筛选公司
            df['公司'] = df['公司'].astype(str)
            row = None
            for cid in target_company_ids:
                found = df[df['公司'] == cid]
                if not found.empty:
                    row = found.iloc[0]
                    break
            
            if row is None and not df.empty:
                row = df.iloc[0] # Fallback
                
            if row is None:
                return

            try:
                # 获取初盘进球数
                init_goals_str = str(row['初盘进球数'])
                if '/' in init_goals_str:
                    parts = init_goals_str.split('/')
                    init_goals = (float(parts[0]) + float(parts[1])) / 2
                else:
                    init_goals = float(init_goals_str)
                
                # 获取即时进球数
                curr_goals_str = str(row['即时进球数'])
                if '/' in curr_goals_str:
                    parts = curr_goals_str.split('/')
                    curr_goals = (float(parts[0]) + float(parts[1])) / 2
                else:
                    curr_goals = float(curr_goals_str)
                    
                # 核心逻辑: 初盘 >= min 且 即时 <= max
                if init_goals >= self.anomaly_min_initial and curr_goals <= self.anomaly_max_current:
                    match_info = self.get_match_info(match_id)
                    msg = f"走地异常告警: {match_info}\n初盘大小: {init_goals} -> 即时: {curr_goals}\n条件: 初>={self.anomaly_min_initial} 且 即<={self.anomaly_max_current}"
                    self._show_alert(msg, match_id)
                    self._log_to_ui(f"发现异常: {msg}", "WARN")

            except (ValueError, TypeError) as e:
                pass # 数据解析错误忽略

        except Exception as e:
            self._log_to_ui(f"异常监控检查失败 {match_id}: {e}", "ERROR")

    def _monitoring_loop(self):
        """监控循环（在后台线程中运行）"""
        check_interval = 20  # 轮询间隔(秒)，避免请求过于频繁

        while self.is_monitoring:
            try:
                # 0. 自动扫描走地
                if self.auto_add_live:
                    self.scan_for_live_matches()

                if not self.monitored_matches:
                    time.sleep(2)
                    continue
                
                # 日志记录本轮开始
                match_count = len(self.monitored_matches)
                self._log_to_ui(f"开始新一轮盘口轮询，监控 {match_count} 场比赛...", "INFO")

                # 遍历监控的比赛
                for match_id in list(self.monitored_matches):
                    if not self.is_monitoring: break
                    
                    # 1. 检查亚让盘口差异
                    if self.monitor_handicap_diff:
                        self.check_match_odds(match_id)
                    
                    # 2. 检查总进球盘口差异
                    if self.monitor_total_goals_diff:
                        self.check_match_overdown_odds(match_id)
                    
                    # 3. 检查走地异常 (如果开启)
                    if self.enable_anomaly_monitor:
                        self.check_match_anomaly(match_id)
                    
                    # 稍微暂停一下，避免请求过于密集
                    time.sleep(1.5) 

            except Exception as e:
                error_msg = f"监控主循环错误: {str(e)}"
                self._log_to_ui(error_msg, "ERROR")

            # 等待下一轮
            for _ in range(check_interval):
                if not self.is_monitoring: break
                time.sleep(1)

    def _show_alert(self, message, match_id=None):
        """显示盘口差异告警"""
        # 检查是否在冷却期
        if match_id:
            last_time = self.last_notification_time.get(match_id)
            if last_time and datetime.now() - last_time < timedelta(minutes=30):
                # 冷却期内，只记录日志，不弹窗
                self._log_to_ui(f"[通知冷却] {message}", "WARN")
                return

            # 更新最后通知时间
            self.last_notification_time[match_id] = datetime.now()

        self._log_to_ui(message, "ERROR")

        # ==========================================
        # 触发通知
        # ==========================================
        global_config = get_global_config()
        audio_config = global_config.get_audio_config()
        email_config = global_config.get_email_config()
        
        # 音频通知
        if audio_config.get('enabled', False):
            try:
                audio_file = audio_config.get('sound_file')
                if not audio_file: # 如果没有自定义文件，使用默认路径
                    base_dir = get_base_dir()
                    audio_file = os.path.join(base_dir, "static", "audio", "notify.wav")
                
                audio_sender = AudioSender()
                audio_sender.send("盘口告警", "", [], sound_file=audio_file)
                self._log_to_ui("已发送音频告警", "INFO")
            except Exception as e:
                self._log_to_ui(f"发送音频告警失败: {e}", "ERROR")

        # 邮件通知
        if email_config.get('enabled', False):
            try:
                smtp_host = email_config.get('host')
                smtp_port = email_config.get('port')
                smtp_username = email_config.get('username')
                smtp_password = email_config.get('password')
                use_tls = email_config.get('use_tls')
                email_recipient = email_config.get('recipients', []) # 假设配置文件中也有收件人字段

                if not email_recipient:
                    email_recipient = smtp_username # 如果没有设置收件人，默认发给自己

                email_sender = EmailSender(
                    host=smtp_host,
                    port=smtp_port,
                    username=smtp_username,
                    password=smtp_password,
                    use_tls=use_tls
                )
                
                match_info_str = self.get_match_info(match_id) if match_id else "未知比赛"
                email_subject_template = email_config.get('subject_template', "足球盘口告警: {match_info}")
                email_body_template = email_config.get('body_template', "{message}")

                email_subject = email_subject_template.format(match_info=match_info_str)
                email_body = email_body_template.format(message=message, match_info=match_info_str)
                
                email_sender.send(email_subject, email_body, [email_recipient])
                self._log_to_ui(f"已发送邮件告警到 {email_recipient}", "INFO")
            except Exception as e:
                self._log_to_ui(f"发送邮件告警失败: {e}", "ERROR")
        # ==========================================

        # 弹出通知窗口 - 同样需要线程安全
        def show_box_and_highlight():
            if hasattr(self.match_viewer, 'show_alert_dialog'):
                self.match_viewer.show_alert_dialog("盘口差异告警", message, match_id=match_id)
            else:
                pass
        
        self.match_viewer.root.after(0, show_box_and_highlight)

    def _log_to_ui(self, message, level="INFO"):
        """线程安全地添加日志"""
        if self.match_viewer and hasattr(self.match_viewer, 'add_log'):
            self.match_viewer.root.after(0, lambda: self.match_viewer.add_log(message, level))

    def get_monitoring_status(self):
        """获取监控状态"""
        return {
            'is_monitoring': self.is_monitoring,
            'monitored_matches': len(self.monitored_matches),
            'monitoring_companies': len(self.monitoring_companies),
            'threshold': self.odds_threshold
        }